<template>
  <div class="container">
    <div class="top_num">100.2</div>
    <div
      class="index_num"
      :style="{
        borderColor: borderColor,
        color: textColor
      }"
    >
      {{ computedIndicate.toFixed(1) }}
    </div>
    <canvas ref="dashboardCanvas" width="140" height="140"></canvas>
  </div>
</template>

<script setup>
import { onMounted, ref, defineProps, watch, computed } from 'vue'

/// 动态计算边框颜色和字体颜色
const borderColor = computed(() => {
  return 'white'
})

const textColor = computed(() => {
  return 'white'
})

const computedIndicate = ref(0)

// 定义props，传入indicate，默认值为0
const props = defineProps({
  indicate: {
    type: Number,
    default: 0
  }
})

const dashboardCanvas = ref(null)
const pointerColor = ref('white') //指针颜色
const backgroundColor = ref('#666666') //仪表盘背景颜色
const numberColor = ref('white') //仪表盘数字颜色
// 当前指针角度（从0开始）
let currentAngle = 0
let targetAngle = 0
let speed = 0
let radius = 0 //画布直径
let dashBoardRadius = 0 //仪表盘的直径
let centerX = 0 //圆心横坐标
let centerY = 0 //圆心纵坐标
const EPSILON = 1e-10 // 0.0000000001标识精度
const dashBoardArc = Math.PI * 1.15 //仪表盘弧度

// 初始化仪表盘
onMounted(() => {
  computedIndicate.value = props.indicate
  const canvas = dashboardCanvas.value
  if (canvas.getContext) {
    const ctx = canvas.getContext('2d')
    radius = canvas.width / 2
    dashBoardRadius = radius - 20
    centerX = canvas.width / 2
    centerY = canvas.height / 2
    drawDashboard(ctx, canvas.width, canvas.height)
  }
})

// 监听indicate的变化
watch(
  () => props.indicate,
  (newVal, oldVal) => {
    if (newVal <= 110) {
      const canvas = dashboardCanvas.value
      const ctx = canvas.getContext('2d')
      if (newVal - oldVal != 0) {
        targetAngle = (dashBoardArc * newVal) / 110
        speed = Math.abs((targetAngle - currentAngle) / 2000)
        changeDashboard(ctx, canvas.width, canvas.height)
      }
    }
  }
)

const drawPointer = (ctx) => {
  // 绘制背景
  ctx.beginPath()
  ctx.moveTo(centerX, centerY) // 从圆心开始
  ctx.arc(centerX, centerY, dashBoardRadius, 0, currentAngle)
  ctx.lineTo(centerX + radius * Math.cos(currentAngle), centerY + radius * Math.sin(currentAngle))
  ctx.fillStyle = backgroundColor.value
  ctx.fill()
  ctx.closePath()
  //绘制指针
  ctx.beginPath()
  ctx.moveTo(centerX, centerY)
  ctx.lineTo(
    centerX + (radius - 15) * Math.cos(currentAngle),
    centerY + (radius - 15) * Math.sin(currentAngle)
  )
  ctx.lineWidth = 2
  ctx.strokeStyle = pointerColor.value
  ctx.stroke()
  ctx.closePath()
}

const drawTargetPointer = (ctx, step) => {
  // 绘制指针
  ctx.beginPath()
  if (step > 0) {
    ctx.arc(centerX, centerY, radius - 15, currentAngle, targetAngle)
  } else {
    ctx.arc(centerX, centerY, radius - 15, targetAngle, currentAngle)
  }
  ctx.lineWidth = 2
  ctx.strokeStyle = pointerColor.value
  ctx.stroke()

  ctx.moveTo(
    centerX + dashBoardRadius * Math.cos(targetAngle),
    centerY + dashBoardRadius * Math.sin(targetAngle)
  )
  ctx.lineTo(
    centerX + (radius - 15) * Math.cos(targetAngle),
    centerY + (radius - 15) * Math.sin(targetAngle)
  )
  ctx.lineWidth = 2
  ctx.strokeStyle = pointerColor.value
  ctx.stroke()
  ctx.closePath()
}

//绘制仪表盘动画
const changeDashboard = (ctx, width, height) => {
  // 插值：逐渐调整currentAngle到targetAngle

  let step = (targetAngle - currentAngle) * speed // 插值步长（可以调整以控制动画速度）
  if (step > 0.01) {
    step = 0.01
  }
  if (step < -0.01) {
    step = -0.01
  }
  // 如果角度差值很小，停止动画
  if (Math.abs(step) < EPSILON) {
    currentAngle = targetAngle
    drawDashboard(ctx, width, height)
    drawPointer(ctx)
    return
  }
  // 更新currentAngle
  currentAngle += step
  computedIndicate.value = (currentAngle / dashBoardArc) * 110

  // // 绘制仪表盘和指针
  drawDashboard(ctx, width, height)
  drawTargetPointer(ctx, step)
  drawPointer(ctx)
  // 递归调用，更新下一帧
  requestAnimationFrame(() => changeDashboard(ctx, width, height))
}

// 绘制仪表盘函数
const drawDashboard = (ctx, width, height) => {
  // 清空画布
  ctx.clearRect(0, 0, width, height)
  ctx.beginPath()
  ctx.arc(centerX, centerY, dashBoardRadius, 0, dashBoardArc)
  ctx.lineWidth = 1
  ctx.strokeStyle = numberColor.value
  ctx.stroke()
  ctx.closePath()
  ctx.beginPath()
  //画绿色和黄色警戒线
  const greenLine = (dashBoardArc * 100) / 110
  ctx.moveTo(
    centerX + dashBoardRadius * Math.cos(greenLine),
    centerY + dashBoardRadius * Math.sin(greenLine)
  )
  ctx.lineTo(
    centerX + (radius - 30) * Math.cos(greenLine),
    centerY + (radius - 30) * Math.sin(greenLine)
  )
  ctx.lineWidth = 2
  ctx.strokeStyle = 'yellow'
  ctx.stroke()
  ctx.closePath()
  ctx.beginPath()

  ctx.moveTo(
    centerX + dashBoardRadius * Math.cos(greenLine),
    centerY + dashBoardRadius * Math.sin(greenLine)
  )
  ctx.lineTo(
    centerX + (dashBoardRadius + 5) * Math.cos(greenLine),
    centerY + (dashBoardRadius + 5) * Math.sin(greenLine)
  )

  ctx.lineWidth = 2
  ctx.strokeStyle = '#00DD00'
  ctx.stroke()
  ctx.lineTo(
    centerX +
      (dashBoardRadius + 5) * Math.cos(greenLine) +
      10 * Math.cos(Math.PI * 0.1 + greenLine),
    centerY + (dashBoardRadius + 5) * Math.sin(greenLine) + 10 * Math.sin(Math.PI * 0.1 + greenLine)
  )
  ctx.lineWidth = 2
  ctx.strokeStyle = '#00DD00'
  ctx.stroke()
  ctx.moveTo(
    centerX + (dashBoardRadius + 5) * Math.cos(greenLine),
    centerY + (dashBoardRadius + 5) * Math.sin(greenLine)
  )
  ctx.lineTo(
    centerX +
      (dashBoardRadius + 5) * Math.cos(greenLine) +
      10 * Math.cos(greenLine - Math.PI * 0.1),
    centerY + (dashBoardRadius + 5) * Math.sin(greenLine) + 10 * Math.sin(greenLine - Math.PI * 0.1)
  )
  ctx.lineWidth = 2
  ctx.strokeStyle = '#00DD00'
  ctx.stroke()
  ctx.closePath()
  ctx.beginPath()
  // //画红色警戒线
  const redAngele = Math.PI * 0.15
  ctx.moveTo(
    centerX - dashBoardRadius * Math.cos(redAngele),
    centerY - dashBoardRadius * Math.sin(redAngele)
  )
  ctx.lineTo(
    centerX - (dashBoardRadius + 10) * Math.cos(redAngele),
    centerY - (dashBoardRadius + 10) * Math.sin(redAngele)
  )
  ctx.lineWidth = 2
  ctx.strokeStyle = 'red'
  ctx.stroke()
  ctx.closePath()
}
</script>

<style scoped>
.container {
  width: 100px;
  display: flex;
  align-items: center; /* 垂直居中 */
  justify-content: center; /* 水平居中 */
  position: relative; /* 容器的定位上下文 */
}
.top_num {
  position: absolute;
  top: 20px;
  left: 58px;
  color: #00dd00;
  font-weight: 500;
}
.index_num {
  width: 55px;
  height: 22px;
  border: solid 1px white;
  border-radius: 2px;
  position: absolute;
  top: 47px;
  left: 49px;
  text-align: right;
  padding-right: 10px;
  line-height: 18px;
}
</style>
